/*
 * @(#)PalindromeArray.java	1.0 Apr 26, 2008
 *
 *	The MIT License
 *
 *	Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com>
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *	THE SOFTWARE.
 */
package org.eoti.math;

import java.math.BigInteger;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

public class PalidromeArray
	implements Iterable<BigInteger>
{
	protected static final BigInteger TWO = BigInteger.valueOf(2);
	protected ConcurrentHashMap<BigInteger,BigInteger> array;
	protected BigInteger totalLength, halfLength;
	protected boolean isEven = true;

	public static PalidromeArray fromString(String s)
	{
		String[] arr = s.split("\\|");
		BigInteger[] bi = new BigInteger[arr.length];
		for(int i=0; i<arr.length; i++)
			bi[i] = new BigInteger(arr[i]);

		return new PalidromeArray(bi);
	}

	public PalidromeArray(PalidromeArray array)
	{
		this.totalLength = array.totalLength;
		this.halfLength = array.halfLength;
		this.isEven = array.isEven;

		this.array = new ConcurrentHashMap<BigInteger,BigInteger>();
		for(BigInteger index : array.array.keySet())
			this.array.put(index, array.array.get(index));
	}

	public PalidromeArray(BigInteger[] array)
	{
		this.totalLength = BigInteger.valueOf(array.length);
		this.halfLength = totalLength.divide( TWO );
		if( MathUtil.isOdd(totalLength))
		{
			isEven = false;
			halfLength=halfLength.add(BigInteger.ONE);
		}
		this.array = new ConcurrentHashMap<BigInteger,BigInteger>();

		BigInteger index = BigInteger.ZERO;
		for(BigInteger bi : array)
		{
			this.array.put( index, bi );
			index = index.add(BigInteger.ONE);
		}
	}

	public PalidromeArray(BigInteger totalLength)
	{
		this.totalLength = totalLength;
		this.halfLength = totalLength.divide( TWO );
		if( MathUtil.isOdd(totalLength))
		{
			isEven = false;
			halfLength=halfLength.add(BigInteger.ONE);
		}
		array = new ConcurrentHashMap<BigInteger,BigInteger>();
	}

	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for(BigInteger bi : this)
		{
			if(!first) sb.append("|");
			sb.append(bi);
			first = false;
		}

		return sb.toString();
	}

	public BigInteger halfLength(){return halfLength;}
	public BigInteger totalLength(){return totalLength;}
	public BigInteger get(BigInteger position)
	{
		/**
		 * {1,4,6,4,1} would have {1,4,6} in our array
		 * 0: return 1
		 * 1: return 4
		 * 2: return 6
		 * 3: return 4
		 * 4: return 1
		 * totalLength = 5
		 * halfLength = 3
		 * get(0) returns #0
		 * get(1) returns #1
		 * get(2) returns #2
		 * get(3) returns #1
		 * get(4) returns #0
		 *
		 * {1,3,3,1} would have {1,3} in our array
		 * array.length = 2
		 * 0: return 1
		 * 1: return 3
		 * 2: return 3
		 * 3: return 1
		 * totalLength = 4
		 * halfLength = 2
		 * get(0) returns #0
		 * get(1) returns #1
		 * get(2) returns #1
		 * get(3) returns #0
		 */
		if(position.subtract(halfLength).signum() < 0)
			return array.get(position);

		BigInteger mid = halfLength.subtract(BigInteger.ONE);

		if(isEven)
			return array.get( mid.subtract( position.subtract(halfLength) ));

		return array.get( mid.subtract( position.subtract(mid) ));
	}
	public void set(BigInteger position, BigInteger value){array.put(position, value);}

	public Iterator<BigInteger> iterator()
	{
		return new PalidromeArrayIterator(this);
	}

	class PalidromeArrayIterator
	implements Iterator<BigInteger>
	{
		protected PalidromeArray array;
		protected BigInteger position = BigInteger.ZERO;

		public PalidromeArrayIterator(PalidromeArray array)
		{
		    this.array = array;
		}

		public boolean hasNext()
		{
			return array.totalLength.subtract(position).signum() > 0;
		}

		public BigInteger next()
		{
			BigInteger index = position;
			position = position.add(BigInteger.ONE);
			return array.get(index);
		}

		public void remove()
		{
			throw new UnsupportedOperationException("Not supported");
		}
	}
}
